#include "KohonenLayer.h"
#include "NeighborhoodFunctions/GaussianFunction.h"

using namespace NeuralPlusPlus::Core;
using namespace NeuralPlusPlus::Core::SOM;
using namespace NeuralPlusPlus::Core::SOM::NeighborhoodFunctions;

PositionNeuron* NeuralPlusPlus::Core::SOM::KohonenLayer::operator()( int x, int y )
	{
	return (PositionNeuron*)Neurons[x + y * size.Width];
	}

NeuralPlusPlus::Core::SOM::KohonenLayer::KohonenLayer( Size size, INeighborhoodFunction *neighborhoodFunction /*= NULL*/, LatticeTopologyType topology /*= LatticeTopology::Rectangular*/ ) : Layer(size.Width * size.Height)
	{
	// The product can be positive when both width and height are negative. So, we need to check one.
	Helper::ValidatePositive(size.Width, "size.Width");

	Helper::ValidateNotNull(neighborhoodFunction, "neighborhoodFunction");

	this->size = size;
	if(neighborhoodFunction==NULL)
		{
		this->neighborhoodFunction = new GaussianFunction((double)max(size.Width, size.Height) / 2.0);
		}
	else
		{
		this->neighborhoodFunction = neighborhoodFunction;
		}
	this->topology = topology;

	for (int i = 0; i < size.Height; i++)
		{
		for (int j = 0; j < size.Width; j++)
			{
			Neurons[i*size.Width +j] = (INeuron*)new PositionNeuron(Point(i, j),this);
			}
		}
	}

void NeuralPlusPlus::Core::SOM::KohonenLayer::Initialize()
	{
	//Since there are no initializable parameters in this->layer, this->is a do-nothing function
	}

void NeuralPlusPlus::Core::SOM::KohonenLayer::Run()
	{
	this->winner = (PositionNeuron*)Neurons[0];
	for (int i = 0; i < NeuronsLength; i++)
		{
		PositionNeuron *neuron = (PositionNeuron*)Neurons[i];
		neuron->Run();
		if (neuron->Output < winner->Output)
			{
			winner = (PositionNeuron*)Neurons[i];
			}
		}
	}

void NeuralPlusPlus::Core::SOM::KohonenLayer::Learn( int currentIteration, int trainingEpochs )
	{
	// Validation Delegated
	neighborhoodFunction->EvaluateNeighborhood(this,currentIteration, trainingEpochs);
	__super::Learn(currentIteration, trainingEpochs);
	}

NeuralPlusPlus::Core::SOM::KohonenLayer::~KohonenLayer()
	{
	delete neighborhoodFunction;
	neighborhoodFunction= NULL;
	}